﻿//------------------------------------------------------------------------------
// Copyright © FRA & FV 2014
// All rights reserved
//------------------------------------------------------------------------------
// Binary Serialization Custom Tool
//
// SVN revision information:
//   $Revision: 97 $
//------------------------------------------------------------------------------
using System.CodeDom;
using FRAFV.Binary.Serialization.CodeDom;

namespace FRAFV.Binary.Serialization
{
	/// <summary>
	/// public void InitBinary()
	/// {
	/// 	Property1 = new Item1();
	/// 	Property1.InitBinary();
	/// 	...
	/// 	
	/// 	Property2 = new Type[N];
	/// 	...
	/// }
	/// </summary>
	internal sealed class InitBinaryGenerator : ClassGenerator.MethodGenerator
	{
		public InitBinaryGenerator(ClassParser cls)
			: base(cls) { }

		protected override ClassGenerator.MethodContext CreateMember(ClassParser cls)
		{
			//void InitBinary()
			var code = new CodeMemberMethod
			{
				Name = "InitBinary"
			};
			return new MethodContext(cls, code);
		}

		protected override void Init()
		{
			if (GenerateBaseCall)
			{
				//base.InitBinary();
				Code.BaseMethodInvoke("InitBinary");
			}
		}

		protected override ClassGenerator.PropertyGenerator CreateProperty(ClassParser.ClassProperty prop)
		{
			var context = new ClassGenerator.PropertyContext(this.context, prop);
			if (context.IsItem)
			{
				if (this.HasItemMethod && context.IsRequiredItem)
					return new PropertyItemGenerator(context, CreateItemArray(context));
				else
					return null;
			}
			else if (context.IsArrayItem)
			{
				return new PropertyArrayItemGenerator(context, context.IsArray && context.CountIsNumber ?
					new PropertyArrayItemNumGenerator(context) : null);
			}
			else
			{
				return new PropertyDataGenerator(context, context.IsArray && context.CountIsNumber ?
					new PropertyArrayNumGenerator(context) : null);
			}
		}

		private ClassGenerator.PropertyGenerator CreateItemArray(ClassGenerator.PropertyContext context)
		{
			return null;
		}

		private sealed class MethodContext : ClassGenerator.MethodContext
		{
			public MethodContext(ClassParser cls, CodeMemberMethod member)
				: base(cls, member, member.Statements)
			{
				member.Attributes = MethodsAccess;
			}

			protected override string ResolveItemMethod()
			{
				return Class.BinBlock.ReadItemMethod;
			}
		}

		private sealed class PropertyDataGenerator : ClassGenerator.PropertyDataGenerator
		{
			public PropertyDataGenerator(ClassGenerator.PropertyContext context, ClassGenerator.PropertyGenerator array)
				: base(context, array) { }

			protected override ClassGenerator.FormatGenerator CreateFormat(BinDataAttribute binData)
			{
				var context = new ClassGenerator.FormatContext(this.context, binData);
				if (context.Format.HasLength() && context.LengthIsNumber && !context.IsArray)
					return new FormatLongNumGenerator(context);
				return null;
			}

		}

		//[BinArray(CountCustomMethod = "Number")] Type[] Property
		//[BinArray(CountCustomMethod = "Number")] ArrayType Property
		private sealed class PropertyArrayNumGenerator : ClassGenerator.PropertyGenerator
		{
			public PropertyArrayNumGenerator(ClassGenerator.PropertyContext context)
				: base(context) { }

			protected override CodeStatementCollection GenerateProperty()
			{
				Statements
					//if (Property == null) {
					.Condition(PropertyIsNull).TrueStatements
						.Comment(Name)
						//Property = (PropertyType)new Type[N]
						.Assign(context.Property, Type.CreateArray(CountNumber).Cast(ArrayTypeName));
				return null;
			}

		}

		//[BinArray(CountCustomMethod = "Number"), BinArrayItem] Type[] Property
		//[BinArray(CountCustomMethod = "Number"), BinArrayItem] ArrayType Property
		//[BinArrayItem]
		private sealed class PropertyArrayItemNumGenerator : ClassGenerator.PropertyGenerator
		{
			public PropertyArrayItemNumGenerator(ClassGenerator.PropertyContext context)
				: base(context) { }

			protected override CodeStatementCollection GenerateProperty()
			{
				var statements = Statements
					//if (Property == null) {
					.Condition(PropertyIsNull).TrueStatements;

				var loop = statements
					//var buffer = new Type[N];
					.Variable("buffer", Type.CreateArray(CountNumber))
					//for(int index = 0; index < N; index++)
					.ForCount("index", CountNumber.AsExpression());

				//Property = (PropertyType)buffer;
				statements.Assign(context.Property, "buffer".AsVariable().Cast(ArrayTypeName));
				return loop.Statements;
			}

		}

		//[BinArray, BinArrayItem]
		private sealed class PropertyArrayItemGenerator : ClassGenerator.PropertyArrayItemGenerator
		{
			public PropertyArrayItemGenerator(ClassGenerator.PropertyContext context, ClassGenerator.PropertyGenerator array)
				: base(context, array) { }

			protected override CodeStatementCollection GenerateData()
			{
				if (context.IsConditionArray ||
					context.Property.IsCollection || !context.CountIsNumber) return null;

				Statements.Comment(Name);

				if (!HasCreateCustomMethod)
					//var item = new ItemType();
					Statements.Variable("item", Type.CreateInvoke());
				else
					//var item = Item_Create();
					Statements.Variable("item", CreateCustomMethod);

				//buffer[index] = item;
				Statements.Assign("buffer".AsVariable().Index("index"), "item".AsVariable());

				//item.InitBinary();
				Statements.MethodInvoke("item".AsVariable(), "InitBinary");
				return null;
			}

		}

		private sealed class PropertyItemGenerator : ClassGenerator.PropertyItemGenerator
		{
			public PropertyItemGenerator(ClassGenerator.PropertyContext context, ClassGenerator.PropertyGenerator array)
				: base(context, array) { }

			protected override CodeStatementCollection GenerateData()
			{
				Statements
					//if (Property1 == null) {
					.Condition(PropertyIsNull).TrueStatements
						.Comment(Name)
						//Property1 = new Item1();
						.Assign(context.Property, RequiredItemType.CreateInvoke())
						//Property1.InitBinary();
						.MethodInvoke(Name.AsField(), "InitBinary");

				return null;
			}
		}

		private sealed class FormatLongNumGenerator : ClassGenerator.FormatGenerator
		{
			public FormatLongNumGenerator(ClassGenerator.FormatContext context)
				: base(context) { }

			protected override void GenerateData()
			{
				Statements
					//if (Property == null) {
					.Condition(PropertyIsNull).TrueStatements
						.Comment(PropertyName)
						//Property = (PropertyType)new Type[N];
						.Assign(context.Property, Format.AsType().GetElementType().CreateArray(LengthNumber).Cast(PropertyType));
			}
		}
	}
}
